using System;
using System.Collections.Generic;
using PropertyExpression.Common;

namespace PropertyExpression.ReactiveRelation
{
    internal class CalculatedValueCache<TCalcContext>
    {
        private readonly Dictionary<object, Dictionary<string, ICalculatedValueCacheItem<TCalcContext>>> objectDictionary =
            new Dictionary<object, Dictionary<string, ICalculatedValueCacheItem<TCalcContext>>>();

        public bool Add(ICalculatedValueCacheItem<TCalcContext> calculatedValueCacheItem)
        {
            Dictionary<string, ICalculatedValueCacheItem<TCalcContext>> subObjectDictionary;
            var entity = calculatedValueCacheItem.Entity;
            if (!objectDictionary.TryGetValue(entity, out subObjectDictionary))
            {
                subObjectDictionary = new Dictionary<string, ICalculatedValueCacheItem<TCalcContext>>();
                objectDictionary.Add(entity, subObjectDictionary);
            }
            var propertyName = calculatedValueCacheItem.PropertyName;
            bool result;
            if (!subObjectDictionary.ContainsKey(propertyName))
            {
                subObjectDictionary.Add(propertyName, calculatedValueCacheItem);
                result = true;
            }
            else
            {
                result = false;
            }
            return result;
        }

        public void Calculate(
            IOneToManyCollectionNewItemProcessor oneToManyCollectionNewItemProcessor,
            Func<IBindContext<TCalcContext>> bindContextFunc)
        {
            foreach (var subDictionary in objectDictionary.Values)
            {
                foreach (var calculatedValueCacheItem in subDictionary.Values)
                {
                    calculatedValueCacheItem.SetValue(
                        new CalculatedValueCacheFinder(this),
                        oneToManyCollectionNewItemProcessor, bindContextFunc);
                }
            }
        }

        private class CalculatedValueCacheFinder : ICalculatedValueCacheFinder<TCalcContext>
        {
            private readonly CalculatedValueCache<TCalcContext> parent;

            public CalculatedValueCacheFinder(CalculatedValueCache<TCalcContext> parent)
            {
                this.parent = parent;
            }

            public IOptionalValue<ICalculatedValueCacheItem<TCalcContext>> Find(
                IEntityPropertyProxy entityPropertyProxy)
            {
                Dictionary<string, ICalculatedValueCacheItem<TCalcContext>> subDictionaary;
                if (parent.objectDictionary.TryGetValue(entityPropertyProxy.Entity, out subDictionaary))
                {
                    ICalculatedValueCacheItem<TCalcContext> calculatedValueCacheItem;
                    if (subDictionaary.TryGetValue(entityPropertyProxy.PropertyName, out calculatedValueCacheItem))
                    {
                        return calculatedValueCacheItem.AsOptionalValue();
                    }
                }
                return OptionalValue.Nothing<ICalculatedValueCacheItem<TCalcContext>>();
            }
        }
    }
}